<?php

	abstract class Devmod_base {

		public $db = null; // public cause of inline functions in onEventBind

		public $view = null;

		protected $type = null;
		
		protected $table_struct = null;

		protected $method_objects = array();

		protected $config = array();

		protected $error = array();

		protected $registered_props_output_buffer = array();
		
		public $env_vars = array();
		
		public $helper = null;

		public function __construct()
		{	
			$this->db = ydsql::getInstance(); $this->helper = new Devmod_helper();
		}

		/**
		 * the class for the not existing method will load and return the reference of the class
		 * object:
		 * TODO: is this class available?
		 * TODO: is this class for THIS CURRENT $type available?
		 */
		public function __call($name, $args)
		{
			$class = 'Devmod_method_'.$name;
			
			$method_return = null;
			
			$tmp_obj = new $class($name, $this->type, $this->config, $this->view);

			$tmp_obj->set_config($args); // if there is sth to set for this method: do it now

			/*
			 * new: check if there are cross_calls available. lets say a function wants to add new fields into the fields_config. 
			 * those lines of code should make this possible
			 */
			foreach($tmp_obj->get_cross_calls() as $function => $args)
			{
				call_user_func_array(array($this, $function), $args);
			}
			
		// 1.) exec method operation	
			
			if(method_exists($tmp_obj, 'operation_' . $this->type))
			{
				$method_return = call_user_func_array(array($tmp_obj, 'operation_' . $this->type), array());
				// TODO: this method wants to be called with types type. there is an operation. what now??? [$method_return]
			}

		// 2.) exec for the method registred props
			$method_return = $this->check_registered_props($name, $method_return, 'after_operation');

			
			// TMP OVERWRITE CURRENT THEME CONFIG
			$this->view->assign('grid_config', $this->config);
			$this->view->assign('this_config', $this->config[$this->type]);
			
		// 3.) check for finalizing methods: if there is none, $finalized = true and the TTL is arrived	
			// if there is an finalize method, call this and do not return object references anymore. return content from finalized
			if(method_exists($tmp_obj, 'finalize_' . $this->type))
			{
				$method_return = call_user_func_array(array($tmp_obj, 'finalize_' . $this->type), array($method_return));
				
				$finalized = true;
			}

		// 4.) check for finalizing bind methods in others
			
			$method_return = $this->check_registered_props($name, $method_return, 'after_finalized');
						
		// 5.) now if finalized was true: return the $return content	
			/*
			 * the finalized methods are called. now we perhaps want to execute some BIND functions (after finalized)
			 */
			
			if(isset($finalized))
			{
				return $method_return;
			}
			
		// 6.) its not finalized yet, so push informations to config, write method object and return RECURSION

			$this->assign_env_vars($tmp_obj->get_vars());
			
			$this->push_to_config($this->type, $tmp_obj->get_config(), $tmp_obj->overwrite_config);
			
			$this->method_objects[$name] = $tmp_obj;
		
			return $this;
		}

		/**
		 * check if for the running method ($called_method) are registered properties
		 * in other methods are available, if yes: call them and return(?)
		 * TODO: what and how do we return the called function stuff?
		 * 
		 * this function will now used for 2 different checks: $after_operation AND $after_finalized
		 */
		protected function check_registered_props($called_method, $called_method_return_args = null, $check_vars_type)
		{
			foreach($this->method_objects as $method => $objects)
			{
				// issue fix with switching between frontend/backend
				$objects->view->theme = 'theme/' . $this->config['theme'];
				$objects->view->template_dir = $objects->view->theme . '/tpl/';
				
				// find out which function we need to call inside of the NOT $called_method
				if(count($objects->{$check_vars_type}) > 0)
				{
					foreach($objects->{$check_vars_type} as $prop)
					{
						if($prop['for_method'] == $called_method && $prop['for_method_type'] == $this->type)
						{
							if(!method_exists($objects, $prop['use']))
							{
								$this->set_error("the method '{$prop['use']}' does not existing in class '{$objects->name}'");
								return false;
							} else {
								$objects->type = $this->type; // set the current type for the executed object
								$objects->update_new_config($this->config); // before the call of the object happens - the newesd config will be imported
								$called_method_return_args = call_user_func_array(array($objects, $prop['use']), array($called_method_return_args));
								$this->assign_env_vars($objects->get_vars());
								$this->assign_smarty_vars($objects->view);
								$this->set_props_output_buffer($called_method_return_args, $called_method, $prop['use'], $method, $prop['for_method'], $prop['for_method_type']);
							}
						}
					}
				}
			}

			return $called_method_return_args;
		}
		
		private function assign_env_vars($object_vars)
		{
			if(count($object_vars) == 0)
				return false;
				
			foreach($object_vars as $key => $value)
			{
				$this->env_vars[$key] = $value;
			}
			
			return true;
		}
		
		/**
		 * assign not assigned vars from the plugins into "this" view.
		 * 
		 * 29.06.2010, bsuter: this is not stable yet in combination with method: addOn()
		 */
		private function assign_smarty_vars($object_view)
		{
			foreach($object_view->getTemplateVars() as $var => $value)
			{
				if(!isset($this->view->tpl_vars[$var]))
				{
					$this->view->assign($var, $value);
				}
				
				if($var == "addon_calls")
				{
					$this->view->assign($var, $value);
					continue;
				}
				
				if(is_array($value) && $var == "tpl_addons")
				{
					//var_dump($value);
					//$value = array_unique($value);
					$this->view->assign($var, array_unique($value));
				}
			}
		}

		/**
		 * set the buffer/return output of a called function through registered_props into array 
		 * $registered_props_output_buffer so we can use those result and return values later
		 */
		private function set_props_output_buffer($return, $run_at_method, $used_method, $called_method, $for_method, $for_method_type)
		{
			$this->registered_props_output_buffer[] = array(
				'return' => $return, 
				'run_at_method' => $run_at_method,
				'to_call_method' => $called_method,
				'to_call_method_function_used' => $used_method,
				'for_method' => $for_method,
				'for_method_type' => $for_method_type
			);
		}

		/**
		 * this public function set the current type. old name in grid was $this->tmp
		 * TODO: check if this $type is available in $this->config
		 */
		public function type($type)
		{
			$this->type = $type; return $this;
		}

		/**
		 * insert an array into the config. is used by returning from the __call after $obj->set_config()
		 * TODO: check if this config $array is already available in the config for this $type
		 * 
		 * @param $overwrite [bool] if true (which is method default) array_merge_recursive is allowed
		 */
		protected function push_to_config($type, array $array, $overwrite)
		{
			if(!array_key_exists($type, $this->config))
			{
				$this->set_error("the type $type is not available in the config");
				return false;
			}
			
			foreach($array as $key => $arr)
			{
				if($key == "operator") // operator config changes do not contain any type ==> special event
				{
					$this->config['operator'][$type] = $arr;
				} else {
					if(array_key_exists($key, $this->config[$type]) && $overwrite == false)
					{
						$this->config[$type] = array_merge_recursive($this->config[$type], array($key => $arr));
					} else {
						$this->config[$type][$key] = $arr;
					}		
				}
			}
			return true;
		}

		/**
		 * set an error message. check if errors are available befor fetching template
		 */
		protected function set_error($text)
		{
			$this->error[] = $text;
		}
		
		public function start()
		{
			$this->view->assign('grid_config', $this->config);
			$this->view->assign('this_config', $this->config[$this->type]);
			return $this->view->fetch('common/grid_show.tpl');
		}

		public function generate_checksum()
		{
			return md5(serialize($this->config['sql_table']));
		}

		/*
		 *********** DEV & DEBUG ***********
		 */

		public function export_dev()
		{
			echo "<h1>ERROR_VAR</h1><p>"; print_r($this->error);
			echo "</p><h1>REGISTERED_PROPS_VAR</h1><p>";
			print_r($this->registered_props_output_buffer);
			echo "</p><h1>ACTIV_CONFIG_VAR</h1><p>"; print_r($this->config);
			echo "</p>";
		}

	}
